package net.alpenviewer.map;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;

import javax.imageio.ImageIO;


/**
 * Major interest is the height between 1000m and 5000 m. 
 *
 */
public final class CommonElevationColorEncoder extends ColorEncoder {

	private static final int DEFAULT_MAX_ELEVATION = 8848;
	private static final int DEFAULT_MIN_ELEVATION = 500;	
	private static final int DEFAULT_NULL_ELEVATION = 200;
	private static final int NULL_COLOR = (new Color(255, 255, 255, 0)).getRGB();
	
	private static final int palette_size = 768;
	private static final float h_begin   =   80.f / 360.f;
	private static final float h_end     = -100.f / 360.f;	
	
	private static final float s_begin   = 0.9f;
	private static final float s_end     = 0.8f;
	
	private static final float b_begin   = 0.85f;
	private static final float b_end     = 0.95f;
	
	private static final float h_interp_factor  = 0.65f;
	private static final float s_interp_factor  = 1.00f;
	private static final float b_interp_factor  = 0.60f;

	private final ColorMap cm;
	final int minElevation;
	final int maxElevation;
	final int nullElevation;

	public CommonElevationColorEncoder() {
		this(DEFAULT_MAX_ELEVATION, DEFAULT_MIN_ELEVATION, DEFAULT_NULL_ELEVATION);
	}

	/**
	 * Color will mapped between maxElevation and minElevation.
	 * The elevation shorter than minElevation is faded out till nullElevation (with whole transparent).
	 */
	private CommonElevationColorEncoder(int maxElevation, int minElevation, int nullElevation) {

		if (maxElevation > minElevation && minElevation > nullElevation) {
			// ok
		} else {
			throw new IllegalArgumentException("Wrong init elevation values");
		}

		int[] palette = new int[palette_size];
		for ( int i=0; i<palette_size; i++ ) {
			final float var_h = (float)Math.pow((float)i / (float)palette_size, h_interp_factor);        	
			final float var_s = (float)Math.pow((float)i / (float)palette_size, s_interp_factor);
			final float var_b = (float)Math.pow((float)i / (float)palette_size, b_interp_factor);
			final float h = h_begin + var_h * (h_end - h_begin);        	
			final float s = s_begin + var_s * (s_end - s_begin);
			final float b = b_begin + var_b * (b_end - b_begin);
			palette[i] = ColorLib.hsb(h, s, b);
		}        

		this.minElevation = minElevation;
		this.maxElevation = maxElevation;
		this.nullElevation = nullElevation;
		cm = new ColorMap(palette, minElevation, maxElevation);

	}

	@Override public int getEncodedColor(double elevation) {
		if (elevation < nullElevation) {
			return NULL_COLOR;
		} else if (elevation < minElevation){			
			return ColorLib.interp(NULL_COLOR, 
								   cm.getColor(minElevation), 
								   (float)(elevation - nullElevation) / (float)(minElevation - nullElevation));
		}
		return cm.getColor(elevation);
	}

	
	@Override public BufferedImage getVerticalLegend(int width, int height, Object... parameters) {
		boolean isMeter = (Boolean)parameters[0];
		
		/* original resolution */
		final int imgH = maxElevation - minElevation + 1;
		final int imgW = 1;		
		final BufferedImage img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
		for (int y=0; y<imgH; y++) { 
			for (int x=0; x<imgW; x++) {
				img.setRGB(x, imgH - y - 1, getEncodedColor((short)(y+minElevation)));				
			}
		}

		/* scaled */
		final BufferedImage legend = ColorEncoder.resize(img, width, height);
		Graphics2D g = legend.createGraphics();
		g.setFont(new Font("Sans", Font.PLAIN, 10));
		g.setColor(new Color(0, 0, 0, 120));
		{			
			double meterToUnit = 1;
			int step = 1000;
			String unit = "m";

			if (!isMeter) {
				meterToUnit = 3.2808399;
				step = 3000;
				unit = "ft";	
			}
			final int minLabel = (int) Math.ceil( (double)this.minElevation*meterToUnit / (double)step );
			final int maxLabel = (int) Math.floor( (double)this.maxElevation*meterToUnit / (double)step );
			for (int i=minLabel; i <= maxLabel; i++) {
				int h = (int)((i * step - minElevation*meterToUnit) * height / ((maxElevation - minElevation)*meterToUnit));
				String hString = i*step + unit;
				final int offset = height - h; 
				g.drawLine(0, offset, 3, offset);
				g.drawString(hString, 6, offset);
			}	

		}		
		g.dispose();
		return legend;
	}


	public static void main(String[] args) throws Exception {
		CommonElevationColorEncoder cm = new CommonElevationColorEncoder();
		RenderedImage img = cm.getVerticalLegend(50, 120, true);
		File out = new File("test_output/legendInMeter.png");
		ImageIO.write(img, "PNG", out);
		
		RenderedImage img2 = cm.getVerticalLegend(50, 120, false);
		File out2 = new File("test_output/legendInFeet.png");
		ImageIO.write(img2, "PNG", out2);		
	}
	


}